package org.witness.informacam.storage; import info.guardianproject.iocipher.VirtualFileSystem; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.Vector; import org.apache.commons.io.IOUtils; import org.witness.informacam.InformaCam; import org.witness.informacam.intake.DCIMObserver; import org.witness.informacam.models.Model; import org.witness.informacam.models.j3m.IDCIMDescriptor; import org.witness.informacam.models.media.IAsset; import org.witness.informacam.utils.Constants.App; import org.witness.informacam.utils.Constants.App.Storage; import org.witness.informacam.utils.Constants.App.Storage.Type; import org.witness.informacam.utils.Constants.InformaCamEventListener; import org.witness.informacam.utils.Constants.Models; import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.util.Log; public class IOService { private VirtualFileSystem vfs = null; private DCIMObserver dcimObserver = null; private List<java.io.File> cleanupQueue = new Vector<java.io.File>(); private final static String LOG = App.Storage.LOG; private Context mContext = null; public IOService (Context context) { mContext = context; } public void onDestroy() { if(vfs != null) { vfs.unmount(); } for(java.io.File f : cleanupQueue) { Log.d(LOG, "removing unsafe file: " + f.getAbsolutePath()); f.delete(); } } public boolean saveBlob(InputStream data, IAsset asset) throws IOException { if(asset.source == Type.IOCIPHER) { return saveBlob(data, new info.guardianproject.iocipher.File(asset.path)); } else { return saveBlob(data, new java.io.File(asset.path), true); } } public boolean saveBlob(byte[] data, IAsset asset) throws IOException { if(asset.source == Type.IOCIPHER) { return saveBlob(data, new info.guardianproject.iocipher.File(asset.path)); } else { return saveBlob(data, new java.io.File(asset.path), true); } } public boolean saveBlob(byte[] data, java.io.File file, boolean isPublic) throws IOException { if(!isPublic) { return saveBlob(data, file); } else { try { java.io.FileOutputStream fos = new java.io.FileOutputStream(file); fos.write(data); fos.flush(); fos.close(); return true; } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return false; } } public boolean saveBlob(InputStream data, java.io.File file, boolean isPublic) throws IOException { if(!isPublic) { return saveBlob(data, file); } else { try { java.io.FileOutputStream fos = new java.io.FileOutputStream(file); IOUtils.copyLarge(data, fos); fos.flush(); fos.close(); return true; } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return false; } } public boolean saveBlob(byte[] data, java.io.File file) throws IOException { return saveBlob(data, file, null); } public boolean saveBlob(InputStream data, java.io.File file) throws IOException { return saveBlob(data, file, null); } public boolean saveBlob(InputStream data, java.io.File file, String uriToDelete) throws IOException { java.io.FileOutputStream fos = mContext.openFileOutput(file.getName(), mContext.MODE_PRIVATE); IOUtils.copyLarge(data, fos); fos.flush(); fos.close(); if(uriToDelete != null) { mContext.getContentResolver().delete(Uri.parse(uriToDelete), null, null); } return true; } public boolean saveBlob(byte[] data, java.io.File file, String uriToDelete) throws IOException { java.io.FileOutputStream fos = mContext.openFileOutput(file.getName(), mContext.MODE_PRIVATE); IOUtils.write(data, fos); fos.flush(); fos.close(); if(uriToDelete != null) { mContext.getContentResolver().delete(Uri.parse(uriToDelete), null, null); } return true; } public boolean saveBlob(byte[] data, info.guardianproject.iocipher.File file) { try { return saveBlob(data, file, null); } catch (IOException ioe) { Log.e(LOG,"iocipher saveState() error",ioe); return false; } } public boolean saveBlob(byte[] data, info.guardianproject.iocipher.File file, String uri) throws IOException { ByteArrayInputStream bais = new ByteArrayInputStream(data); return saveBlob (bais, file, uri); } public boolean saveBlob(InputStream data, info.guardianproject.iocipher.File file) throws IOException { return saveBlob(data, file, null); } public boolean saveBlob(InputStream is, info.guardianproject.iocipher.File file, String uri) throws IOException { if(vfs == null) { // TODO: this should throw exception return false; } info.guardianproject.iocipher.FileOutputStream fos = new info.guardianproject.iocipher.FileOutputStream(file); IOUtils.copyLarge(is, fos); if(uri != null) { mContext.getContentResolver().delete(Uri.parse(uri), null, null); } return true; } public boolean saveBlob(Model model, java.io.File file) throws IOException { return saveBlob(model.asJson().toString().getBytes(), file); } public boolean saveBlob(Model model, info.guardianproject.iocipher.File file) throws IOException { return saveBlob(model.asJson().toString().getBytes(), file); } public boolean isAvailable(String pathToData, int source) { Object file = null; switch(source) { case Storage.Type.IOCIPHER: file = new info.guardianproject.iocipher.File(pathToData); if(((info.guardianproject.iocipher.File) file).length() > 0) { return true; } break; case Storage.Type.FILE_SYSTEM: file = new java.io.File(pathToData); if(((java.io.File) file).length() > 0) { return true; } break; } return false; } public byte[] getBytes(IAsset asset) { return getBytes(asset.path, asset.source); } public byte[] getBytes(String pathToData, int source) { byte[] bytes = new byte[0]; switch(source) { case Storage.Type.INTERNAL_STORAGE: java.io.FileInputStream fis; try { fis = mContext.openFileInput(pathToData); bytes = new byte[fis.available()]; fis.read(bytes); fis.close(); } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } break; case Storage.Type.IOCIPHER: if(vfs == null) { return null; /* Log.d(LOG, "also, VFS IS NULL SO..."); InformaCam informaCam = (InformaCam)getApplication(); if(!informaCam.attemptLogin()) { informaCam.promptForLogin(); return null; }*/ } info.guardianproject.iocipher.FileInputStream iFis; info.guardianproject.iocipher.File file = new info.guardianproject.iocipher.File(pathToData); try { iFis = new info.guardianproject.iocipher.FileInputStream(file); bytes = new byte[iFis.available()]; iFis.read(bytes); iFis.close(); } catch (FileNotFoundException e) { Log.d(LOG, "no, no bytes (" + pathToData + ")"); return null; } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (Exception e) { Log.e(LOG, e.toString()); } break; case Storage.Type.APPLICATION_ASSET: try { InputStream is = mContext.getAssets().open(pathToData, Context.MODE_PRIVATE); bytes = new byte[is.available()]; is.read(bytes); is.close(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } break; case Storage.Type.CONTENT_RESOLVER: break; case Storage.Type.FILE_SYSTEM: try { java.io.File file_ = new java.io.File(pathToData); java.io.FileInputStream fis_ = new java.io.FileInputStream(file_); bytes = new byte[fis_.available()]; fis_.read(bytes); fis_.close(); } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } break; } // Log.d(LOG, "(" + pathToData + ") bytes here: " + bytes.length); return bytes; } public InputStream getStream(IAsset asset) throws IOException { return getStream(asset.path, asset.source); } public InputStream getStream(String pathToData, int source) throws IOException { InputStream is = null; switch(source) { case Storage.Type.INTERNAL_STORAGE: java.io.FileInputStream fis; try { fis = mContext.openFileInput(pathToData); is = fis; } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } break; case Storage.Type.IOCIPHER: if(vfs == null) { return null; /* Log.d(LOG, "also, VFS IS NULL SO..."); InformaCam informaCam = (InformaCam)getApplication(); if(!informaCam.attemptLogin()) { informaCam.promptForLogin(); return null; }*/ } info.guardianproject.iocipher.File file = new info.guardianproject.iocipher.File(pathToData); try { is = new info.guardianproject.iocipher.FileInputStream(file); //Log.d(LOG, "getting Stream (" + pathToData + ") available: " + is.available()); } catch (FileNotFoundException e) { //Log.d(LOG, "no, no bytes (" + pathToData + ")"); return null; } catch (Exception e) { //Log.e(LOG, e.toString()); } break; case Storage.Type.APPLICATION_ASSET: try { is = mContext.getAssets().open(pathToData, Context.MODE_PRIVATE); } catch (IOException e) { Log.e(LOG, e.toString(),e); } break; case Storage.Type.CONTENT_RESOLVER: break; case Storage.Type.FILE_SYSTEM: java.io.File file_ = new java.io.File(pathToData); is = new java.io.FileInputStream(file_); break; } return is; } public long getLength(String pathToData, int source) { long fileLength = -1; switch(source) { case Storage.Type.INTERNAL_STORAGE: java.io.File fileData; fileData = new java.io.File(pathToData); fileLength = fileData.length(); break; case Storage.Type.IOCIPHER: if(vfs == null) { return -1; /* Log.d(LOG, "also, VFS IS NULL SO..."); InformaCam informaCam = (InformaCam)getApplication(); if(!informaCam.attemptLogin()) { informaCam.promptForLogin(); return null; }*/ } info.guardianproject.iocipher.File file = new info.guardianproject.iocipher.File(pathToData); fileLength = file.length(); break; case Storage.Type.APPLICATION_ASSET: java.io.File fileAsset = new java.io.File(pathToData); fileLength = fileAsset.length(); break; case Storage.Type.CONTENT_RESOLVER: break; case Storage.Type.FILE_SYSTEM: java.io.File file_ = new java.io.File(pathToData); fileLength = file_.length(); break; } return fileLength; } public java.io.File getPublicCredentials() { byte[] publicCredentialsBytes = getBytes(Models.IUser.PUBLIC_CREDENTIALS, Type.IOCIPHER); if(publicCredentialsBytes != null) { try { java.io.File externalDir = new java.io.File(Storage.EXTERNAL_DIR); if(!externalDir.exists()) { externalDir.mkdir(); } java.io.File publicCredentials = new java.io.File(Storage.EXTERNAL_DIR, "publicCredentials.zip"); java.io.FileOutputStream fis = new java.io.FileOutputStream(publicCredentials); fis.write(publicCredentialsBytes); fis.flush(); fis.close(); return publicCredentials; } catch (FileNotFoundException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } catch (IOException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } } return null; } public boolean initIOCipher(byte[] authToken) { try { java.io.File fileExternal = mContext.getExternalFilesDir(null);//Storage.ROOT); if (fileExternal == null) { fileExternal = mContext.getFilesDir(); } java.io.File storageRoot = new java.io.File(fileExternal, Storage.IOCIPHER); vfs = VirtualFileSystem.get(); if (!storageRoot.exists()) vfs.createNewContainer(storageRoot.getAbsolutePath(), authToken); if (!vfs.isMounted()) vfs.mount(storageRoot.getAbsolutePath(),authToken); info.guardianproject.iocipher.File organizationRoot = new info.guardianproject.iocipher.File(Storage.ORGS_ROOT); if(!organizationRoot.exists()) { organizationRoot.mkdir(); } info.guardianproject.iocipher.File formsRoot = new info.guardianproject.iocipher.File(Storage.FORM_ROOT); if(!formsRoot.exists()) { formsRoot.mkdir(); } return true; } catch(IllegalArgumentException e) { Log.e(LOG, e.toString()); e.printStackTrace(); } return false; } public boolean clear(String pathToDirectory, int source) { List<String> files = new ArrayList<String>(); switch(source) { case Storage.Type.IOCIPHER: for(info.guardianproject.iocipher.File file : ((info.guardianproject.iocipher.File) new info.guardianproject.iocipher.File(pathToDirectory)).listFiles()) { files.add(file.getAbsolutePath()); } break; } for(String file : files) { delete(file, source); } return false; } public boolean delete(IAsset asset) { return delete(asset.path, asset.source); } public boolean delete(String pathToFile, int source) { // TODO: MAKE SURE FILE IS OBLITERATED! switch(source) { case Storage.Type.INTERNAL_STORAGE: return mContext.deleteFile(pathToFile); case Storage.Type.IOCIPHER: info.guardianproject.iocipher.File file_ioc = new info.guardianproject.iocipher.File(pathToFile); if(file_ioc.isDirectory()) { for(info.guardianproject.iocipher.File f : file_ioc.listFiles()) { f.delete(); } } return file_ioc.delete(); case Storage.Type.CONTENT_RESOLVER: return mContext.getContentResolver().delete(Uri.parse(pathToFile), null, null) > 0 ? true : false; case Storage.Type.FILE_SYSTEM: java.io.File file_jif = new java.io.File(pathToFile); if(file_jif.isDirectory()) { for(java.io.File f : file_jif.listFiles()) { f.delete(); } } return file_jif.delete(); default: return false; } } public void unmount() throws IllegalStateException { if (vfs != null) vfs.unmount(); } public void startDCIMObserver(InformaCamEventListener listener, String parentId, ComponentName cameraComponent) { //InformaCam.getInstance().mediaManifest.setAllAsOld(); dcimObserver = new DCIMObserver(mContext, parentId, cameraComponent); } public IDCIMDescriptor getDCIMDescriptor() { return dcimObserver.dcimDescriptor; } public void stopDCIMObserver() { Handler h = new Handler(); h.post(new Runnable() { @Override public void run() { dcimObserver.destroy(); } }); } public boolean isMounted() { if(vfs != null) { return vfs.isMounted(); } return false; } }